package wiiusej.apptest;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import wiiusej.*;
import wiiusej.activeTaskManagement.PermanenSoundController;
import wiiusej.activeTaskManagement.PermanentController;
import wiiusej.activeTaskManagement.PermanentLEDContoller;
import wiiusej.activeTaskManagement.PermanentRumbleController;
import wiiusej.activeTaskManagement.SharedCell;
import wiiusej.activeTaskManagement.SoundShareCell;
import wiiusej.apptest.activityTasks.ComClient;
import wiiusej.apptest.activityTasks.EnergyTransmisionTask;
import wiiusej.apptest.activityTasks.RealTimeFeedback;
import wiiusej.apptest.activityTasks.RealTimeFeedbackImproved;
import wiiusej.apptest.activityTasks.Spell;
import wiiusej.apptest.activityTasks.SpellGameTask;
import wiiusej.apptest.generalTasks.ActivateSpeaker;
import wiiusej.apptest.generalTasks.ActiveSound;
import wiiusej.apptest.generalTasks.EnableMotionTask;
import wiiusej.apptest.generalTasks.OnMotionSensingTask;
import wiiusej.apptest.generalTasks.VolumeSpeakerTask;
import wiiusej.apptest.statistics.AccelerationPaquet;
import wiiusej.apptest.statistics.AccelerationPaquetList;
import wiiusej.apptest.statistics.AccelerationPaquetsPerSecond;
import wiiusej.apptest.statistics.PacketWraped;
import wiiusej.values.RawAcceleration;
import wiiusej.wiiusejevents.physicalevents.ExpansionEvent;
import wiiusej.wiiusejevents.physicalevents.IREvent;
import wiiusej.wiiusejevents.physicalevents.MotionSensingEvent;
import wiiusej.wiiusejevents.physicalevents.WiimoteButtonsEvent;
import wiiusej.wiiusejevents.utils.WiimoteListener;
import wiiusej.wiiusejevents.wiiuseapievents.ClassicControllerInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.ClassicControllerRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.DisconnectionEvent;
import wiiusej.wiiusejevents.wiiuseapievents.GuitarHeroInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.GuitarHeroRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.NunchukInsertedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.NunchukRemovedEvent;
import wiiusej.wiiusejevents.wiiuseapievents.StatusEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiimoteEvent;

import org.apache.log4j.Category;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.RootLogger;


public class WiimoteEventListener implements WiimoteListener{
	
	
	//Logger
	static Logger fileLogger;
	
	//Logger URL
	private static final String fileURL = "src/test/java/wiiusej/resources/logs/tasks/WiimoteListener";
	
	//Machine States
	private AtomicBoolean speakerStatus = new AtomicBoolean(false);
	private AtomicBoolean rumbleStatus = new AtomicBoolean(true);
	
	Boolean motionButtonState = false;
	Integer captures = 0;
	
	//Concurrent Main Class
	protected static ExecutorService exe;
	
	//Wiimote object representation
	private Wiimote wiimote;
	
	//Speaker objects
	private final Lock speakerLock = new ReentrantLock();
	private final Condition speakerCondition = speakerLock.newCondition();
	private final SharedCell soundCell;
	//Speaker Parameters
	private static final int FIRST_SPEAKER_LEVEL_FREQ = 2000;	
	
	//Rumble objects
	private final Lock rumbleLock = new ReentrantLock();
	private final Condition rumbleCondition = rumbleLock.newCondition();
	private final SharedCell rumbleCell;
	//Rumble Parameters
	private static final int FIRST_RUMBLE_LEVEL_FREQ = 2000;
	
	//Energy Controller
	private AtomicInteger energyCharged = new AtomicInteger();
	
	//RealTime APP modes
	private final int MODE_SLOWMOTION = 0;
	private final int MODE_BIGSOFTMOTION = 1;
	private final int MODE_ROBOTIC = 2;
	
	
	List<AccelerationPaquet> eventList
    	= Collections.synchronizedList(new ArrayList<AccelerationPaquet>());
	
	
	private PacketWraped myPackets;
	
	Vector<AccelerationPaquet> myEventVector;
	
	protected long randomID;
	
	private AccelerationPaquet lastChecked = null;
	
	AccelerationPaquetList accPacketList;
	
	private ConcurrentLinkedQueue<AccelerationPaquetList> syncQueue;
	
	private BlockingQueue<Vector<AccelerationPaquet>> realTimeQueue 
		= new LinkedBlockingDeque<Vector<AccelerationPaquet>>();
	
	
	private BlockingQueue<Integer> gameToEnergy = new LinkedBlockingDeque<Integer>();
	private BlockingQueue<Integer> realTimeToGameController = new LinkedBlockingDeque<Integer>();
	
	private BlockingQueue<Spell> eneergyToComClient;
	
	private BlockingQueue<Integer> toRumbleQ = 
		new LinkedBlockingQueue<Integer>();
	
	private BlockingQueue<Integer> fromRealToSoundQ = 
		new LinkedBlockingQueue<Integer>();

	private BlockingQueue<Integer> toLEDQ = 
		new LinkedBlockingQueue<Integer>();
	
	
	private ConcurrentLinkedQueue<Integer> fromEnergyToGameQ = 
		new ConcurrentLinkedQueue<Integer>();
	
	private long lastSize = 0;
	/*
	private ConcurrentLinkedQueue<Integer> toSoundQ = 
		new ConcurrentLinkedQueue<Integer>();*/
	
	
	
	public WiimoteEventListener(Wiimote wiimote, ExecutorService exe, 
			ConcurrentLinkedQueue<AccelerationPaquetList> syncQueue, BlockingQueue<Spell> eneergyToComClient) {
		
		
		super();		
		//System.out.println("");
		
		
		this.eneergyToComClient = eneergyToComClient;
		this.wiimote = wiimote;
		this.syncQueue = syncQueue;
		this.exe = exe;
		
		accPacketList = new AccelerationPaquetList(wiimote.getId());
		//myEventVector = accPacketList.getPackets();
		
		myPackets = new PacketWraped(accPacketList); 
		
		/*
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("speaker");*/
		
		Random random = new Random(System.currentTimeMillis());
		this.randomID = random.nextLong();
		
		this.rumbleCell = new SharedCell(FIRST_RUMBLE_LEVEL_FREQ);		
		this.soundCell = new SharedCell(FIRST_SPEAKER_LEVEL_FREQ);
		
		
		PropertyConfigurator.configure("log4j.properties");
		this.fileLogger = Logger.getLogger("wiimoteListener");
		
		PatternLayout pattern = 
			new PatternLayout("%r %-5p %c %x - %m%n");		
		
		try {			
			//Creating a file for every connected wiimote
			FileAppender fileappend=new FileAppender(pattern,fileURL + wiimote.getId() + ".log");
			fileLogger.addAppender(fileappend);
		} catch (IOException e) {
			e.printStackTrace();
		}		
		
		initComponents();
		
		
	}
	
	

	private void initComponents(){
	
		
		RealTimeFeedbackImproved realTimeApp = new RealTimeFeedbackImproved(randomID, soundCell, 
				wiimote, this, realTimeQueue, realTimeToGameController, fromRealToSoundQ);
		
		realTimeApp.start();
		
		Runnable gameController = 
			new SpellGameTask(realTimeToGameController, gameToEnergy, toRumbleQ, fromEnergyToGameQ, wiimote);
		
		exe.execute(gameController);
		
		/*
		Runnable ledController = 
			new PermanentLEDContoller(wiimote, toLEDQ);
		
		exe.execute(ledController);*/
		
		Runnable energyTransmision = 
			new EnergyTransmisionTask(soundCell, wiimote, gameToEnergy, eneergyToComClient, 
					toRumbleQ, toLEDQ, fromEnergyToGameQ);
		
		exe.execute(energyTransmision);
		
		initSpeakerController();
		//initRumbleController();
		registerListeners();		
		activateMotionSensing();
		
		activateSpeaker();
		setSoundMax();
		
		
		/*
		Runnable clientServer = 
			new ComClient(eneergyToComClient);
		
		exe.execute(clientServer);*/
		
		
	}
	
	protected void initSpeakerController(){		
		//fileLogger.info("Setting Up Permanent Sound");		
		
		PermanenSoundController speakerController = (PermanenSoundController) 
			new PermanenSoundController(this, wiimote, soundCell, speakerLock, speakerCondition, fromRealToSoundQ);		
		
		exe.execute(speakerController);
	}
	
	protected void initRumbleController(){
		
		PermanentRumbleController rumbleController = 
			new PermanentRumbleController(this, wiimote, rumbleLock, rumbleCell, 
					rumbleCondition, toRumbleQ, exe);
		
		exe.execute(rumbleController);
		
	}
	
	private void setSoundMax(){
		int i;
		
		for(i = 0; i<=7; i++){
			wiimote.turnVolumeUp();			
		}
		
	}
	
	
	protected void registerListeners() {
		//We have to add our listener to the wiimote object
		wiimote.addWiiMoteEventListeners(this);		
	}
	protected void activateMotionSensing() {
		wiimote.activateMotionSensing();		
	}

	//Button Listener - will act as a State Machine
	public void onButtonsEvent(WiimoteButtonsEvent arg0) {
	    //System.out.println(arg0);
	    
	    //ENABLE MOTION CAPTURING
	    if (arg0.isButtonAJustPressed()){   	
	    	
	    	try {
				gameToEnergy.put(0);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	    	
	    	
	    }	    
	    
	    //ACTIVATING SPEAKER
	    if (arg0.isButtonOneJustPressed()){	    	
	    	//activateSpeaker();	    	
	    }	   
	    
	    //SPEAKER VOLUME CONTROL (TURN UP/DOWN)
	    if( (arg0.isButtonPlusJustPressed())){
			//turnUpVolume(arg0);	    						    	
		} 
		if((arg0.isButtonMinusJustPressed())){
			//turnDownVolume(arg0);								    	
		}	    
	    
	}
	
	private void activateMotion(){
		
		Future<Boolean> future = 
    		exe.submit(new EnableMotionTask(fileLogger, motionButtonState));	    	
    	
		
    	try {
			motionButtonState = future.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}	  
		
	}
	
	public void activateSpeaker(){
		
		fileLogger.debug("speakerStatus: " + speakerStatus);
    	
    	Future<AtomicBoolean> future = 
    		exe.submit(new ActivateSpeaker(wiimote, speakerStatus));    		
    		
    	try {
    		speakerStatus = future.get();	    		
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}			
		
		fileLogger.debug("SpeakerStatus After Activation: " + speakerStatus);
		
		
		//fileLogger.debug("Waking Up Permanent Sound");
		
		//wait until get the lock
		//fileLogger.debug("Getting SPEAKER lock");
		speakerLock.lock();
		
		try{
			//fileLogger.debug("SPEAKER condition signalling");
			speakerCondition.signalAll();				
		}finally{
			//fileLogger.debug("Releasing SPEAKER lock");
			speakerLock.unlock();				
		}		
		//Runnable permanentSound = new ActiveSound(wiimote);
		//exe.execute(permanentSound);	
		
	}
	
	private void turnUpVolume(WiimoteButtonsEvent arg0){
		//fileLogger.debug("SPEAKER: pressing Volume UP. SpeakerStatus: " + speakerStatus);	    	
    	Future<Boolean> future = 
			exe.submit(new VolumeSpeakerTask(wiimote, speakerStatus, arg0));
		
	}
	
	private void turnDownVolume(WiimoteButtonsEvent arg0){
		//fileLogger.debug("SPEAKER: pressing Volume DOWN. SpeakerStatus: " + speakerStatus);			
		Future<Boolean> future = 
			exe.submit(new VolumeSpeakerTask(wiimote, speakerStatus, arg0));	
	}
	
	//Motion Sensing Listener - depends on the state will capture motion measurements
	public void onMotionSensingEvent(MotionSensingEvent arg0) {		
		
		fileLogger.info("EventReceived - Wiimote: " + arg0.getWiimoteId() +
				" Packet: " + arg0.getRefNumber() +
				" Time: " + arg0.getTime());		
		
		fileLogger.info(" ");
		
		getStatistics(arg0);
		
		//syncQueue.offer(accPacketList);
		
		realTimeApp(arg0);
		
		
    }	
	
	
	
	
	public boolean isSpeakerStatus() {
		return speakerStatus.get();
	}
	
	public boolean isRumbleStatus() {
		return rumbleStatus.get();
	}
	
	
	private void getStatistics(MotionSensingEvent arg0){
		
		Future future = 
			exe.submit(new AccelerationPaquetsPerSecond(arg0, wiimote.getId(), randomID, myPackets));	
		
		try {
			//eventList = (List<AccelerationPaquet>) future1.get();
			//accPacketList.setPackets((Vector<AccelerationPaquet>) future.get());
			myPackets = (PacketWraped) future.get();
			
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		} catch (ExecutionException e1) {
			e1.printStackTrace();
		}		
		
		
		
	}
	
	private void realTimeApp(MotionSensingEvent arg0){
		
		/*
		//need to receive a mode in order to run the game
		Future future = 
			exe.submit(new RealTimeFeedbackImproved(accPacketList.getPackets(), randomID, lastChecked, 
					soundCell, wiimote, this, MODE_SLOWMOTION));
			*/
		//if(accPacketList.getPackets().size() > lastSize+1){		
			try {
				//realTimeQueue.put(accPacketList.getPackets());
				realTimeQueue.put(myPackets.getMyEventVector().getPackets());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//lastSize = accPacketList.getPackets().size();
		//}
		
		
		/*
		try{
			//lastChecked = (AccelerationPaquet) future.get();		
			fileLogger.info(" LastChecked: " + lastChecked.getTime());
		}catch(Exception ex){
			ex.printStackTrace();
		}*/
	}
	
	private void chargeWiimote(MotionSensingEvent arg0){
		
		fileLogger.info("Calling OnMotionSensingTask");
		
		
		Future<Integer> future2 = 
    		exe.submit(new OnMotionSensingTask(wiimote, arg0, motionButtonState));    	
		
		try {			
			if(future2.get() != 0){    			
    			
				energyCharged.set(energyCharged.get() + future2.get());    			
    			
    			fileLogger.info("EnergyCharged Upload: " + energyCharged.get());
    			fileLogger.info("Calling EnergyTransmission");
    			
    			/*
    			Runnable energyTransmision = 
    				new EnergyTransmisionTask(energyCharged, soundCell, rumbleCell, wiimote, realTimeToEnergy);*/
    			
    			//exe.execute(energyTransmision);
    		}    		
    		
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}	
		
	}
	
	//REST OF INTERFACE IMPLEMENTATION
	@Override
	public void onClassicControllerInsertedEvent(
			ClassicControllerInsertedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onClassicControllerRemovedEvent(
			ClassicControllerRemovedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onDisconnectionEvent(DisconnectionEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onExpansionEvent(ExpansionEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onGuitarHeroInsertedEvent(GuitarHeroInsertedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onGuitarHeroRemovedEvent(GuitarHeroRemovedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onIrEvent(IREvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onNunchukInsertedEvent(NunchukInsertedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onNunchukRemovedEvent(NunchukRemovedEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onStatusEvent(StatusEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	
	@Override
	public String toString(){		

		return Integer.toString(wiimote.getWiimoteId());
	}

	
	
	
}


